package com.cizzy.video.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.nacos.shaded.com.google.gson.Gson;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.cizzy.video.common.Result;
import com.cizzy.video.entity.dao.UserCountDAO;
import com.cizzy.video.entity.dao.UserDAO;
import com.cizzy.video.entity.dao.VideoCountDAO;
import com.cizzy.video.entity.dao.VideoDAO;
import com.cizzy.video.entity.dto.UserDTO;
import com.cizzy.video.entity.dto.VideoDTO;
import com.cizzy.video.entity.vo.PageRequest;
import com.cizzy.video.entity.vo.VideoVo;
import com.cizzy.video.feign.UserFeign;
import com.cizzy.video.mapper.VideoCountMapper;
import com.cizzy.video.mapper.VideoMapper;
import com.cizzy.video.service.IVideoService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qiniu.common.QiniuException;
import com.qiniu.http.Response;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.Region;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.DefaultPutRet;
import com.qiniu.util.Auth;
import io.seata.spring.annotation.GlobalTransactional;
import jakarta.annotation.Resource;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.sql.Timestamp;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static com.cizzy.video.config.CizzyFinal.*;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author crissy-月下闲人
 * @since 2023-10-29
 */
@Service
public class VideoServiceImpl extends ServiceImpl<VideoMapper, VideoDAO> implements IVideoService {
    @Autowired
    private VideoMapper videoMapper;

    @Autowired
    private VideoCountMapper videoCountMapper;

    @Resource
    private UserFeign userFeign;

    @Resource
    RedissonClient redissonClient;

    @Override
    public Result<List<VideoDAO>> init() {
        return null;
    }

    @Override
    public synchronized Result<VideoDAO> createVideo(VideoDAO video) {
        // 创建QueryWrapper对象
        QueryWrapper<VideoDAO> queryWrapper = new QueryWrapper<>();
        // 设置VideoDAO对象的创建时间为当前时间
        video.setCreateAt(new Timestamp(System.currentTimeMillis()));
        // 调用videoMapper的insert方法插入video对象到数据库中
        int result = videoMapper.insert(video);
        // 如果插入成功，返回成功结果
        if (result > 0) {
            return Result.success();
        } else {
            // 如果插入失败，返回失败结果并提示失败原因
            return Result.error("视频创建失败");
        }
    }

    @Override
    public Result<List<VideoDAO>> getRandVideoList(Integer videoNum) {
        QueryWrapper<VideoDAO> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByAsc("id").
                orderByAsc("RAND()").
                last(String.format("LIMIT %d", videoNum));
        return getVideoListResult(videoNum, queryWrapper);
    }

    @Override
    public Result<List<VideoDAO>> getVideoList(Integer videoNum) {
        QueryWrapper<VideoDAO> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc("video_id").last(String.format("LIMIT %d", videoNum));
        return getVideoListResult(videoNum, queryWrapper);
    }

    @Override
    public Result<List<VideoDAO>> getVideoPage(Integer page, Integer pageSize) {
        QueryWrapper<VideoDAO> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc("video_id").
                last(String.format("LIMIT %d,%d", (page - 1) * pageSize, page * pageSize));
        return getVideoListResult(pageSize, queryWrapper);
    }

    @Override
//    @SentinelResource(value = "getVideoById", blockHandler = "getVideoByIdBlockhandler")
    public Result<VideoDAO> getVideoById(Long id) {
        VideoDAO video = videoMapper.selectById(id);
        if (video == null) {
            return Result.error("视频不存在");
        } else {
            return Result.success(video);
        }
    }

    @Override
    public Result<VideoDTO> getVideoDTOById(Long id) {
        VideoDAO video = videoMapper.selectById(id);
        if (video == null) {
            return Result.error("视频不存在");
        }

        VideoCountDAO videoCount = videoCountMapper.selectById(id);
        if (videoCount == null) {
            return Result.error("视频不存在");
        }

        UserDAO user = userFeign.getUserById(video.getUserId()).getData();
        if (user == null) {
            return Result.error("用户服务查询失败");
        }

        VideoDTO videoDTO = new VideoDTO();
        videoDTO.convertFromVideoDAO(video);
        videoDTO.convertFromVideoCountDAO(videoCount);
        videoDTO.convertFromUserDAO(user);
        return Result.success(videoDTO);
    }

    //    @Override
//    public Result<VideoDAO> getVideoByIdBlockhandler(BlockException e) {
//        VideoDAO video = new VideoDAO();
//        video.setVideoId(0L);
//        return Result.success(video);
//    }

    @Override
    public Result<VideoDAO> getVideoByType(String type) {
        QueryWrapper<VideoDAO> queryWrapper = new QueryWrapper<>();
        type = type.replaceAll("0", "_");
        queryWrapper.like("type", type).orderByDesc("video_id");
        VideoDAO video = videoMapper.selectOne(queryWrapper);
        if (video == null) {
            return Result.error("视频不存在");
        } else {
            return Result.success(video);
        }
    }

    @Override
    public Result<List<VideoDAO>> getVideoPageByType(String type, Integer page, Integer pageSize) {
        QueryWrapper<VideoDAO> queryWrapper = new QueryWrapper<>();
        type = type.replaceAll("0", "_");
        queryWrapper.like("type", type).
                orderByDesc("video_id").
                last(String.format("LIMIT %d,%d", (page - 1) * pageSize, page * pageSize));
        return getVideoListResult(pageSize, queryWrapper);
    }

    @Override
    public Result<List<VideoDAO>> getVideoListByType(String type, Integer videoNum) {
        QueryWrapper<VideoDAO> queryWrapper = new QueryWrapper<>();
        type = type.replaceAll("0", "_");
        queryWrapper.like("type", type).
                orderByDesc("video_id").
                last(String.format("LIMIT %d", videoNum));
        return getVideoListResult(videoNum, queryWrapper);
    }

    @Override
    public Result<VideoDAO> removeVideoById(Long id) {
        return null;
    }

    @Override
    public Result<VideoDAO> updateVideo(VideoDAO video) {
        return null;
    }

    @Override
    public Result<List<VideoDTO>> getVideoListByIds(List<Long> ids) {
        List<VideoDAO> videoList = videoMapper.selectBatchIds(ids);
        return getVideoDTOListResultByVideoDAOList(videoList);
    }

    @Override
    public Result<List<VideoDTO>> getVideoListByUserId(Long id) {
        QueryWrapper<VideoDAO> queryWrapper = new QueryWrapper<VideoDAO>().eq("user_id", id);
        List<VideoDAO> videoList = videoMapper.selectList(queryWrapper);
        return getVideoDTOListResultByVideoDAOList(videoList);
    }

    @Override
    public Result<VideoDTO> getRecommendVideo(Long userId) {
        QueryWrapper<VideoDAO> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByAsc("RAND()").
                last(String.format("LIMIT %d", 1));
        VideoDAO video = videoMapper.selectOne(queryWrapper);
        if (video == null) {
            return Result.error("视频不存在");
        }
        VideoCountDAO videoCount = videoCountMapper.selectById(video.getVideoId());
        if (videoCount == null) {
            return Result.error("视频计数不存在");
        }
        UserDAO user = userFeign.getUserById(video.getUserId()).getData();
        if (user == null) {
            return Result.error("用户服务查询失败");
        }
        return Result.success(new VideoDTO(video, videoCount, user));
    }

        /**
     * 获取动态流中的视频列表
     * @param pageRequest 分页请求
     * @return 结果对象，包含视频列表
     */
    @Override
    public Result<List<VideoDTO>> feed(PageRequest pageRequest) {
        Integer page = pageRequest.getPage();
        if (page <= 0) {
            return Result.error("页码不为正数");
        }
        String type = pageRequest.getType();
        List<VideoDAO> videos;
        System.out.println(type);
        if (type == null || type.length() != 10) {
            videos = this.getVideoPage(page, PAGE_SIZE).getData();
        } else {
            videos = this.getVideoPageByType(type, page, PAGE_SIZE).getData();
        }

        if (CollectionUtil.isEmpty(videos)) {
            return Result.success("没有更多视频！", null);
        }

        // 获取视频计数信息
        List<Long> videoIds = videos.stream().map(VideoDAO::getVideoId).toList();
        List<VideoCountDAO> videoCounts = videoIds.stream().map(videoId -> {
            return videoCountMapper.selectById(videoId);
        }).toList();

        if (videoCounts.isEmpty()) {
            return Result.error("视频计数查询失败");
        } else if (videoCounts.size() != videoIds.size()) {
            return Result.error("视频计数查询与视频查询不匹配");
        }

        // 获取用户信息
        List<Long> userIds = videos.stream().map(VideoDAO::getUserId).toList();
        List<UserDTO> users = userFeign.getUserListByIds(userIds).getData();
        if (users == null || users.isEmpty()) {
            return Result.error("用户查询失败");
        }

        List<UserCountDAO> userCounts = userFeign.getUserCountListByIds(userIds).getData();
        if (userCounts == null || userCounts.isEmpty()) {
            return Result.error("用户计数查询失败");
        }

        if (users.size() != userCounts.size()) {
            return Result.error("用户查询与用户计数查询不匹配");
        }

        if (users.size() != videos.size()) {
            return Result.error("用户查询与视频查询不匹配");
        }

        // 构建VideoDTO对象列表
        List<VideoDTO> videoDTOList = IntStream.range(0, Math.min(users.size(), videos.size())).mapToObj(i -> {
            VideoDTO videoDTO = new VideoDTO();
            videoDTO.setAuthor(users.get(i));
            videoDTO.convertFromVideoDAO(videos.get(i));
            videoDTO.convertFromVideoCountDAO(videoCounts.get(i));
            return videoDTO;
        }).toList();
        if (CollectionUtil.isEmpty(videoDTOList)) {
            return Result.success("没有更多视频", null);
        }
        return Result.success(videoDTOList);
    }


    //    @GlobalTransactional
        /**
     * 发布视频
     * @param videoVo 视频信息
     * @param userId 用户ID
     * @return 结果对象
     * @throws IOException 输入参数异常抛出IOException
     */
    @Override
    public synchronized Result<VideoDAO> publishVideo(VideoVo videoVo, String userId) throws IOException {
        if (videoVo == null) {
            return Result.error("视频信息为空");
        }

        VideoDAO video = new VideoDAO(videoVo);
        video.setUserId(Long.valueOf(userId));
        MultipartFile file = videoVo.getFile();

        String filename = file.getOriginalFilename();
        String flag = IdUtil.fastSimpleUUID();
        // TODO: 修改缓存地址，上传结束后决定是否要删除缓存
//        String rootFilePath = System.getProperty("user.dir") + "/src/main/resources/files/" + flag + video.getUserId() + "_" + filename;
//        System.out.println("rootFilePath: " + rootFilePath);
        String rootFilePath = VIDEO_UPLOAD_PATH + flag + video.getUserId() + "_" + filename;
        FileUtil.writeBytes(file.getBytes(), rootFilePath);

        // 配置对应服务器信息
        Configuration cfg = new Configuration(Region.region2());
        cfg.resumableUploadAPIVersion = Configuration.ResumableUploadAPIVersion.V2;// 指定分片上传版本
        //...其他参数参考类注释
        UploadManager uploadManager = new UploadManager(cfg);
        //...生成上传凭证，然后准备上传
//        String accessKey = "1GUt5U3GZPZp0pJdIgaSMsbaDwTBUA3yOBoeZQGB";
//        String secretKey = "tpGy4ROSVdlXSfM6MUHUilRu96f2bnulyDgkyaSM";
//        String bucket = "video31";
        //如果是Windows情况下，格式是 D:\\qiniu\\test.png
        //默认不指定key的情况下，以文件内容的hash值作为文件名
//        String key = video.getUserId() + "_" + video.getCreateAt();
//        rootFilePath = "D:\\BaiduNetdiskDownload\\97.mp4";
        Auth auth = Auth.create(ACCESS_KEY, SECRET_KEY);
        String upToken = auth.uploadToken(BUCKET);
        DefaultPutRet putRet;
        try {
            Response response = uploadManager.put(rootFilePath, null, upToken);
            //解析上传成功的结果
            putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
            video.setCryptoTitle(putRet.hash);
            video.setVideoUrl(QINIU_PRE + putRet.hash);
        } catch (QiniuException ex) {
            ex.printStackTrace();
            if (ex.response != null) {
                System.err.println(ex.response);
                try {
                    String body = ex.response.toString();
                    System.err.println(body);
                } catch (Exception ignored) {
                }
            }
        }

        RLock lock = redissonClient.getLock(VIDEO_LOCK);
        lock.lock();
        videoMapper.insert(video);
        VideoCountDAO videoCount = new VideoCountDAO(video.getVideoId());
        videoCountMapper.insert(videoCount);
        lock.unlock();

        return Result.success(video);
    }


    @Override
    public Result<List<VideoDTO>> searchVideoPage(String keyword, Long videoNum) {
        if (keyword == null || videoNum == null) {
            return Result.error("参数错误");
        }
        QueryWrapper<VideoDAO> queryWrapper = new QueryWrapper<VideoDAO>();
        queryWrapper.last(String.format("LIMIT %d", SEARCH_LIMIT)).
                like("title", "%" + keyword + "%").or().like("description", "%" + keyword + "%");
        List<VideoDAO> videoList = videoMapper.selectList(queryWrapper);
        return getVideoDTOListResultByVideoDAOList(videoList);
    }

    @Override
    public Result<List<Long>> getRandomVideoIdList(Integer num) {
        QueryWrapper<VideoDAO> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByAsc("RAND()").
                last(String.format("LIMIT %d", num));
        List<VideoDAO> videoList = videoMapper.selectList(queryWrapper);
        if (CollectionUtil.isEmpty(videoList)) {
            return Result.success("查询到空视频列表", null);
        }
        return Result.success(videoList.stream().map(VideoDAO::getVideoId).toList());
    }

    private Result<List<VideoDTO>> getVideoDTOListResultByVideoDAOList(List<VideoDAO> videoList) {
        if (videoList == null) {
            return Result.error("查询视频失败");
        }
        List<UserDTO> userDTOList = userFeign.getUserListByIds(videoList.stream().map(VideoDAO::getUserId).toList()).getData();
        List<VideoCountDAO> videoCountList = videoList.stream().map(video -> new VideoCountDAO(video.getVideoId())).toList();
        List<VideoDTO> videoDTOList = IntStream.range(0, Math.min(userDTOList.size(), videoList.size())).mapToObj(i -> {
            VideoDTO videoDTO = new VideoDTO();
            videoDTO.setAuthor(userDTOList.get(i));
            videoDTO.convertFromVideoDAO(videoList.get(i));
            videoDTO.convertFromVideoCountDAO(videoCountList.get(i));
            return videoDTO;
        }).toList();
        if (CollectionUtil.isEmpty(videoDTOList)) {
            return Result.success("没有更多视频", null);
        }
        return Result.success(videoDTOList);
    }

    private Result<List<VideoDAO>> getVideoListResult(Integer videoNum, QueryWrapper<VideoDAO> queryWrapper) {
        List<VideoDAO> videos = videoMapper.selectList(queryWrapper);
        if (videos == null) {
            return Result.error("未读取到视频列表");
        }
        if (videos.isEmpty()) {
            return Result.error("视频列表为空");
        } else if (videos.size() < videoNum) {
            return Result.success("视频数量不足", videos);
        } else {
            return Result.success(videos);
        }
    }
}
